<?php

namespace App\Jobs;

use App\Ad\Docking\DockingBase;
use App\Commons\CommonConstant;
use App\Models\AdMonitor;
use App\Models\AdPlan;
use App\Models\FirstupAd;
use App\Models\Log\LogAdPostbackMedia;
use App\Models\Log\LogAdPostbackUser;
use App\Models\Orders;
use App\Models\OrdersCallClient;
use App\Models\UserExtend;
use App\Structs\AdPostbackPayStruct;
use App\Structs\AdPostbackStatusStruct;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class AdPostbackToMediaQueue implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    use TraitQueue;

    protected $logId;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($id)
    {
        $this->logId = $id;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $log = LogAdPostbackMedia::find($this->logId);
        $notify = $this->postback($log);

        $log->status       = $notify->status;
        $log->call_amount  = $notify->callAmonut;
        $log->status_time  = time();
        $log->check_num   += 1;
        $log->save();
    }

    // sdk回传，把支付落地待通知前端列表
    private function postbackBySdk($order, $call_amount)
    {
        $call = OrdersCallClient::where('order_id', $order->id)->first();
        if ($call) {
            return;
        }

        $call              = new OrdersCallClient;
        $call->create_time = time();
        $call->user_id     = $order->user_id;
        $call->order_id    = $order->id;
        $call->call_amount = $call_amount;
        $call->save();
    }

    private function postback(LogAdPostbackMedia $log): AdPostbackStatusStruct
    {
        $notify_ret = new AdPostbackStatusStruct();

        // 处理类
        $modMaps = [
            CommonConstant::EVENT_FIRSTUP => \App\Models\Firstups::class,
            CommonConstant::EVENT_REG => \App\Models\Users::class,
            CommonConstant::EVENT_LOGIN => \App\Models\UserLogin::class,
            CommonConstant::EVENT_PAY => \App\Models\Orders::class,
        ];

        $plan         = AdPlan::find($log->plan_id);
        $convert_type = $log->convert_type;
        $class        = $modMaps[$convert_type];
        $mod          = $class::find($log->mod_id);

        // 通过配置判断是否需要回传, 仅限于充值回传
        if ($convert_type == CommonConstant::EVENT_PAY) {
            $notify_pay = $this->postbackPayByConfig($log, $mod);
            if ($notify_pay->status === LogAdPostbackMedia::STATUS_NONEEDBYCONFIG) {
                $notify_ret->status = $notify_pay->status;
                return $notify_ret;
            }

            // sdk回传支付
            if ($plan->access_type != AdPlan::ACCESS_TYPE_API) {
                $this->postbackBySdk($mod, $notify_pay->callAmonut);
                $notify_ret->status = LogAdPostbackMedia::STATUS_NOTAPI;
                return $notify_ret;
            }

            // 赋值 call_money，用于下面 postbackToMedia() 回传使用
            // 请勿对mod进行save()操作
            $mod->call_money = $notify_pay->callAmonut;
            $notify_ret->callAmonut = $notify_pay->callAmonut;
        }

        $firstup_ad = FirstupAd::find($log->firstup_ad_id);
        $monitor    = AdMonitor::find($firstup_ad->monitor_id);
        if (!$monitor) {
            $monitor = new AdMonitor;
        }

        $docking = DockingBase::getClass($log->media_docking_id);
        try {
            $status = $docking->postbackToMedia($convert_type, $monitor, $firstup_ad, $mod);
        } catch (\Throwable $th) {
            $status = LogAdPostbackMedia::STATUS_ERROR;
        }

        $notify_ret->status = $status;
        return $notify_ret;
    }

    private function postbackPayByConfig($log, $order)
    {
        $notify = new AdPostbackPayStruct();
        $notify->status = LogAdPostbackMedia::STATUS_NORMAL;  //默认需要回传
        $notify->callAmonut = $order->money;

        $plan = AdPlan::find($log->plan_id);

        // 订单类型
        $goods_type_list = json_decode($plan->postback_goods_type, true);
        if (!empty($goods_type_list)) {
            // 订单类型不匹配无需上报
            if (!in_array($order->goods_type, $goods_type_list)) {
                $notify->status = LogAdPostbackMedia::STATUS_NONEEDBYCONFIG;
                return $notify;
            }
        }

        $config = json_decode($plan->postback_config, true);
        if (!$config) {
            return $notify;
        }

        $user_exetend = UserExtend::where('user_id', $order->user_id)->first();
        if (!$user_exetend) {
            return $notify;
        }

        $postback_type = $plan->postback_type;

        //按默认回传
        if ($postback_type === 0) {
            $is_postback = 1;
            $call_amount = $this->checkHitAndReturnAmount($config, $order->money);
            if (!$call_amount) {
                $is_postback = 0;
                $notify->status = LogAdPostbackMedia::STATUS_NONEEDBYCONFIG;
            }
            $notify->callAmonut = $call_amount;

            if ($order->create_time == $user_exetend->first_pay_time) {
                $this->firstPayPostback($order->user_id, $is_postback);
            }
        }

        //按首充回传
        if ($postback_type == 1) {
            if ($order->create_time == $user_exetend->first_pay_time) {
                $is_postback = 1;
                $call_amount = $this->checkHitAndReturnAmount($config, $order->money);
                if (!$call_amount) {
                    $is_postback = 0;
                    $notify->status = LogAdPostbackMedia::STATUS_NONEEDBYCONFIG;
                }
                $notify->callAmonut = $call_amount;

                $this->firstPayPostback($order->user_id, $is_postback);
            } else {
                $notify->status = LogAdPostbackMedia::STATUS_NONEEDBYCONFIG;
            }
        }

        //按订单回传
        if ($postback_type == 2) {
            $is_postback = 1;
            $call_amount = $this->checkHitAndReturnAmount($config, $order->money);
            if (!$call_amount) {
                $is_postback = 0;
                $notify->status = LogAdPostbackMedia::STATUS_NONEEDBYCONFIG;
            }
            $notify->callAmonut = $call_amount;

            if ($order->create_time == $user_exetend->first_pay_time) {
                $this->firstPayPostback($order->user_id, $is_postback);
            }
        }

        //按用户回传
        if ($postback_type == 3) {
            if ($order->create_time == $user_exetend->first_pay_time) {
                $is_postback = 1;
                $call_amount = $this->checkHitAndReturnAmount($config, $order->money);
                if (!$call_amount) {
                    $is_postback = 0;
                    $notify->status = LogAdPostbackMedia::STATUS_NONEEDBYCONFIG;
                }
                $notify->callAmonut = $call_amount;

                $this->firstPayPostback($order->user_id, $is_postback);
            } else {
                $log = LogAdPostbackUser::where('user_id', $order->user_id)->first();
                if ($log) {
                    if ($log->is_postback == 0) {
                        $notify->status = LogAdPostbackMedia::STATUS_NONEEDBYCONFIG;
                    }
                } else {
                    $notify->status = LogAdPostbackMedia::STATUS_NONEEDBYCONFIG;
                }
            }
        }

        return $notify;
    }

    /**
     * 记录首充是否回传
     */
    private function firstPayPostback($user_id, $is_postback)
    {
        $insert_data = [
            'user_id'     => $user_id,
            'is_postback' => $is_postback,
            'create_time' => time(),
        ];

        LogAdPostbackUser::insert($insert_data);
    }

    // 判断是否命中，并返回需要回传的金额
    private function checkHitAndReturnAmount($config, $money)
    {
        $item = [];
        foreach ($config as $val) {
            if ($money >= $val['min'] && $money < $val['max']) {
                $item = $val;
                break;
            }
            if ($money == $val['min'] && $money == $val['max']) {
                $item = $val;
                break;
            }
        }

        $rand_num = mt_rand(1, 100);
        if (!empty($item) && $rand_num > $item['rate']) {
            return false;
        }

        $money_rate = $item['money_rate'] ?? 100;
        $money = round($money*$money_rate/100, 2);

        return $money;
    }
}
